package com.shiliao.framework.util;

import android.annotation.SuppressLint;
import android.util.Log;

import com.shiliao.framework.FrameworkConfig;
import com.shiliao.framework.base.annotation.CoderClassDesc;
import com.shiliao.framework.base.annotation.CoderMethodDesc;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;

/**
 * log打印类,通过{@code #LOGGABLE}控制是否打印.通过{@code LogUtil#mFILEABLE} 控制是否要将loginfo输出到文件中.
 * 
 * @author
 * 
 */
@CoderClassDesc(author = "dada", desc = "mine desc", date = "2016-9-1")
public final class LogUtil {
    /**
     * 默认的文库日志Tag标签.
     */
    private static String DEFAULT_TAG = "framework";

    public static void setTag(String tag) {
        DEFAULT_TAG = tag;
    }

    /**
     * 是否将log输出到文件中.log文件存储目录为{@link #getFILEABLE()}.
     */
    private static boolean mFILEABLE = getFILEABLE();

    /**
     * 是否要将日志输出到文件中.
     * 
     * @return true if output the log info into files,otherwise return false.
     */
    public static boolean getFILEABLE() {
        File file = new File(FrameworkConfig.DEFAULT_FOLDER + "/_mFILEABLE");
        if (file.exists()) {
            return true;
        }
        return false;
    }

    private LogUtil() {}

    private static StackTraceElement getCallerStackTraceElement() {
        return Thread.currentThread().getStackTrace()[5];
    }

    @CoderMethodDesc(author = "dada", desc = "mine desc", date = "2016-9-1")
    public static String generateTraceTag(StackTraceElement caller, boolean addDesc) {
        String tag = "%s.%s(Line:%d) ---> "; // 占位符
        String callerClazzName = caller.getClassName(); // 获取到类名
        String methodName = caller.getMethodName();
        StringBuilder sb = new StringBuilder();

        if (addDesc) {
            tag = tag + "\n";

            try {
                Class clazz = Class.forName(callerClazzName);
                if (clazz != null) {
                    CoderClassDesc coder =
                            (CoderClassDesc) clazz.getAnnotation(CoderClassDesc.class);
                    if (coder != null) {
                        sb.append("class author: " + coder.author() + "\n");
                        sb.append("class desc: " + coder.desc() + "\n");
                        sb.append("class date: " + coder.date() + "\n");
                    }

                    Method methods[] = clazz.getDeclaredMethods();
                    if (methods != null && methods.length > 0) {
                        for (Method m : methods) {
                            if (!methodName.equalsIgnoreCase(m.getName())) {
                                continue;
                            }

                            CoderMethodDesc coderMehtod =
                                    (CoderMethodDesc) m.getAnnotation(CoderMethodDesc.class);
                            if (coderMehtod != null) {
                                sb.append("method author: " + coderMehtod.author() + "\n");
                                sb.append("method desc: " + coderMehtod.desc() + "\n");
                                sb.append("method date: " + coderMehtod.date() + "\n");

                                break;
                            }

                        }

                    }
                }

            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        callerClazzName = callerClazzName.substring(callerClazzName.lastIndexOf(".") + 1);
        tag = String.format(tag, callerClazzName, methodName, caller.getLineNumber()); // 替换
        if (addDesc) {
            tag = tag + sb.toString();
        }

        return tag;
    }


    private static String getStackTraceInfo() {
        StackTraceElement caller = getCallerStackTraceElement();
        return generateTraceTag(caller, false);

        // StringBuilder sb = new StringBuilder();
        // for (Map.Entry<Thread, StackTraceElement[]> entry : Thread
        // .getAllStackTraces().entrySet()) {
        // sb.append(entry.getKey().getName() + "\n");
        // for (StackTraceElement element : entry.getValue()) {
        // //sb.append("\t" + element + "\n");
        // sb.append(generateTag(element));
        // }
        // }
        //
        // return sb.toString();
    }

    /**
     * 将调用堆栈输出到sd卡中.
     * 
     * @param tag 标识
     * @param str 内容
     * @param hasTrace 是否包含堆栈信息.
     */
    public static void trace2file(String tag, String str, boolean hasTrace) {
        if (FrameworkConfig.getLogEnable()) {
            StringBuilder sb = new StringBuilder();
            sb.append(tag + ":" + str + "\n");
            if (hasTrace) {
                for (Map.Entry<Thread, StackTraceElement[]> entry : Thread.getAllStackTraces()
                        .entrySet()) {
                    sb.append(entry.getKey().getName() + "\n");
                    for (StackTraceElement element : entry.getValue()) {
                        sb.append("\t" + element + "\n");
                    }
                }
            }

            try {
                PrintWriter out =
                        new PrintWriter(new BufferedWriter(new FileWriter(
                                FrameworkConfig.DEFAULT_FOLDER + "/log.txt", true)));
                out.println(sb.toString());
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 打印log到文件中.
     * 
     * @param type 类型
     * @param tag 标识
     * @param value 内容.
     */
    @SuppressLint("SimpleDateFormat")
    public static void logFile(String type, String tag, String value) {
        String now = (new java.text.SimpleDateFormat("yyyy-MM-dd hh:mm:ss")).format(new Date());
        PrintWriter out = null;
        try {
            out =
                    new PrintWriter(new BufferedWriter(new FileWriter(
                            FrameworkConfig.DEFAULT_FOLDER + "/log.txt", true)));
            out.println(String.format("[%s][%s][%s]%s", now, type, tag, value));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (out != null) {
                out.close();
            }
        }
    }

    /**
     * 打印debug级别的log.
     * 
     * @param tag tag标签
     * @param str 内容
     */
    public static void d(String tag, String str) {

        if (FrameworkConfig.getLogEnable()) {
            Log.d(tag, getStackTraceInfo() + str);
        } else if (mFILEABLE) {
            logFile("D", tag, getStackTraceInfo() + str);
        }

        getCallerStackTraceElement();
    }

    /**
     * 打印debug级别的log.
     * 
     * @param str 内容
     */
    public static void d(String str) {
        if (FrameworkConfig.getLogEnable()) {
            Log.d(DEFAULT_TAG, getStackTraceInfo() + str);
        } else if (mFILEABLE) {
            logFile("D", DEFAULT_TAG, getStackTraceInfo() + str);
        }
    }

    /**
     * 打印warning级别的log.
     * 
     * @param tag tag标签
     * @param str 内容
     */
    public static void w(String tag, String str) {

        if (FrameworkConfig.getLogEnable()) {
            Log.w(tag, getStackTraceInfo() + str);
        } else if (mFILEABLE) {
            logFile("W", tag, getStackTraceInfo() + str);
        }
    }

    /**
     * 打印warning级别的log.
     * 
     * @param str 内容
     */
    public static void w(String str) {
        if (FrameworkConfig.getLogEnable()) {
            Log.w(DEFAULT_TAG, getStackTraceInfo() + str);
        } else if (mFILEABLE) {
            logFile("W", DEFAULT_TAG, getStackTraceInfo() + str);
        }
    }

    /**
     * 打印error级别的log.
     * 
     * @param tag tag标签
     * @param msg 内容
     * @param e 错误对象.
     */
    public static void e(String tag, String msg, Throwable e) {
        if (FrameworkConfig.getLogEnable()) {
            Log.e(tag, getStackTraceInfo() + msg, e);
        } else if (mFILEABLE) {
            logFile("E", getStackTraceInfo() + tag, msg);
        }
    }

    /**
     * 打印error级别的log.
     * 
     * @param tag tag标签
     * @param msg 内容
     */
    public static void e(String tag, String msg) {
        if (FrameworkConfig.getLogEnable()) {
            Log.e(tag, getStackTraceInfo() + msg);
        } else if (mFILEABLE) {
            logFile("E", tag, getStackTraceInfo() + msg);
        }
    }

    /**
     * 打印error级别的log.
     * 
     * @param str 内容
     */
    public static void e(String str) {
        if (FrameworkConfig.getLogEnable()) {
            Log.e(DEFAULT_TAG, getStackTraceInfo() + str);
        } else if (mFILEABLE) {
            logFile("E", DEFAULT_TAG, getStackTraceInfo() + str);
        }
    }

    /**
     * 打印info级别的log.
     * 
     * @param tag tag标签
     * @param str 内容
     */
    public static void i(String tag, String str) {
        if (FrameworkConfig.getLogEnable()) {
            Log.i(tag, getStackTraceInfo() + str);
        } else if (mFILEABLE) {
            logFile("I", tag, getStackTraceInfo() + str);
        }
    }

    /**
     * 打印info级别的log.
     * 
     * @param str 内容
     */
    public static void i(String str) {
        if (FrameworkConfig.getLogEnable()) {
            Log.i(DEFAULT_TAG, getStackTraceInfo() + str);
        } else if (mFILEABLE) {
            logFile("I", DEFAULT_TAG, getStackTraceInfo() + str);
        }
    }

    /**
     * 打印verbose级别的log.
     * 
     * @param tag tag标签
     * @param str 内容
     */
    public static void v(String tag, String str) {
        if (FrameworkConfig.getLogEnable()) {
            Log.v(tag, getStackTraceInfo() + str);
        } else if (mFILEABLE) {
            logFile("V", tag, getStackTraceInfo() + str);
        }
    }

    /**
     * 打印verbose级别的log.
     * 
     * @param str 内容
     */
    public static void v(String str) {
        if (FrameworkConfig.getLogEnable()) {
            Log.v(DEFAULT_TAG, getStackTraceInfo() + str);
        } else if (mFILEABLE) {
            logFile("V", DEFAULT_TAG, getStackTraceInfo() + str);
        }
    }

    // public static void logTheadStackTrace() {
    // ArrayList<Map<String, String>> list = AnnotateUtils.getTheadStackTrace();
    // if (list != null) {
    // StringBuilder sb = new StringBuilder();
    // for (Map<String, String> map : list) {
    // sb.append(map.toString() + "\n");
    // }
    //
    // LogUtil.i(sb.toString());
    // }
    //
    // }

}
